第35章 异常处理
在程序运行过程中,难免会出现各种错误(如除以零、数组越界、内存分配失败等)。C++ 提供了异常处理机制,用于在程序出错时捕获错误并进行相应处理,避免程序直接崩溃,提高程序的健壮性和容错能力。
35.1 异常处理的基本概念
异常是程序运行时出现的非正常情况(运行错误)。异常处理核心机制:抛出throw、捕获catch、监测代码块try。
完整流程:try包裹风险代码,出错时执行throw抛出异常;程序跳转至匹配的catch块处理;若无匹配捕获,程序终止。
35.2 异常处理基础语法
try {
// 可能出错的代码
if(错误条件成立){
throw 异常数据;
}
}
catch(异常类型1 变量) {
// 处理类型1异常
}
catch(异常类型2 变量) {
// 处理类型2异常
}
// 可使用catch(...)捕获所有异常
35.2.1 try块
必须紧跟至少一个catch,仅包裹存在运行风险的代码,作用是开启异常监听。
35.2.2 throw语句
主动抛出异常,可抛出任意基础类型、结构体、类对象:
throw 表达式;
抛出的数据会复制一份传递给对应catch。
35.3 基础异常示例(数值错误码)
#include <iostream>
#include <cmath>
using namespace std;
// 计算三角形面积,边长非法抛出错误码
double triangleArea(int a, int b, int c) {
if(a <= 0 || b <=0 || c <=0) {
throw -1; // 边长负数,抛出-1
}
if(a + b <= c || a + c <= b || b + c <= a) {
throw -2; // 无法构成三角形
}
double s = (a + b + 1.0) / 2;
return sqrt(s*(s-a)*(s-b)*(s-c));
}
int main() {
try {
double area = triangleArea(3,4,5);
cout << "三角形面积:" << area << endl;
area = triangleArea(1,1,3); // 触发异常
cout << "三角形面积:" << area << endl;
}catch (int errCode) {
if(errCode == -1) {
cout << "错误:边长必须为正数" << endl;
}else if(errCode == -2) {
cout << "错误:给定边长无法构成三角形" << endl;
}
}
return 0;
}
35.4 自定义异常类
基础类型传递信息有限,自定义类可携带错误代码、错误描述。
#include <iostream>
#include <string>
using namespace std;
class MyException {
private:
int code;
string msg;
public:
MyException(int c, string m): code(c), msg(m) {}
int getCode() const { return code; }
string getMsg() const { return msg; }
};
void openFile(string filename) {
if(filename.empty()) {
throw MyException(1001, "文件名不能为空");
}
if(filename.find(".txt") == string::npos) {
throw MyException(1002, "文件必须是.txt格式");
}
cout << "文件" << filename << "打开成功" << endl;
}
int main() {
try {
openFile("data.doc");
}catch(const MyException& e) {
cout << "错误代码:" << e.getCode() << endl;
cout << "错误信息:" << e.getMsg() << endl;
}
return 0;
}
35.5 异常的传播机制
若当前try-catch没有匹配的异常类型,异常会向上传递到上层调用函数,逐层查找捕获;全程无捕获则程序调用terminate直接结束。
示例:
#include <iostream>
using namespace std;
void func3() {
cout << "进入func3" << endl;
throw "func3中发生异常";
cout << "离开func3" << endl; // 不会执行
}
void func2() {
cout << "进入func2" << endl;
func3();
cout << "离开func2" << endl; // 不会执行
}
void func1() {
cout << "进入func1" << endl;
try {
func2();
}catch(const char* msg) {
cout << "在func1中捕获异常:" << msg << endl;
}
cout << "离开func1" << endl;
}
int main() {
func1();
return 0;
}
输出:
进入func1
进入func2
进入func3
在func1中捕获异常:func3中发生异常
离开func1
35.6 异常处理规则
- 类型严格匹配:
catch捕获类型必须与抛出类型一致;派生类异常可被基类引用捕获。
class Base {};
class Derived : public Base {};
int main() {
try {
throw Derived();
}catch(Base& b) {
cout << "捕获派生类异常" << endl;
}
return 0;
}
- 万能捕获
catch(...):放在所有catch最后,捕获任意类型未知异常。 - 禁止析构函数抛异常:析构抛出异常无捕获会导致资源泄漏、程序崩溃。
- 构造函数可抛异常:构造抛出异常时,对象不会完整创建,已分配资源需手动释放。
- 抛出的异常对象会复制,
catch建议使用引用接收,减少拷贝开销。
35.7 C++标准异常库
头文件<stdexcept>,所有标准异常继承std::exception,通用what()方法返回错误字符串:
| 异常类 | 触发场景 |
|---|---|
| std::out_of_range | 数组/容器下标越界(vector.at) |
| std::invalid_argument | 传入非法参数 |
| std::bad_alloc | new内存分配失败 |
示例:
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
int main() {
try {
vector<int> v(5);
v.at(10) = 1; // 越界抛出out_of_range
}catch(const out_of_range& e) {
cout << "越界错误:" << e.what() << endl;
}
try {
int* p = new int[999999999999]; // 内存不足抛bad_alloc
}catch(const bad_alloc& e) {
cout << "内存分配失败:" << e.what() << endl;
}
return 0;
}
35.8 异常vs错误码对比
- 错误码:函数返回标识,正常业务代码与错误判断大量混杂,代码冗余;适合轻微可预期错误。
- 异常:正常逻辑与错误处理分离,错误自动向上传递;适合不可预测严重运行错误。